package com.nf;

import javax.sql.DataSource;
import java.sql.*;

public class MiniQueryRunner extends MiniAbstractQueryRunner {
    //每个子类的构造函数是一定会调用父类的某个构造函数
    public MiniQueryRunner() {
        super();
    }

    public MiniQueryRunner(boolean pmdKnownBroken) {
        super(pmdKnownBroken);
    }

    public MiniQueryRunner(DataSource ds) {
        super(ds);
    }

    public MiniQueryRunner(boolean pmdKnownBroken, DataSource ds) {
        super(pmdKnownBroken, ds);
    }

    //写类库:把麻烦留给自己，把方便留给别人，解决sql语句中没有问号(无参数delete from xxx where id=1)
    public int update(Connection conn, String sql) throws SQLException {
        return this.update(conn,false,sql,(Object[]) null);
    }

    public int update(Connection con,String sql,Object params) throws SQLException {
        //这里转换
        return this.update(con,false,sql,new Object[]{params});
    }

    //可变长度的参数其实是个数组，所以上面的方法，先转换位数组
    //按照现在的知识，前面的2个方法是多余的，可以删除
    public int update(Connection conn, String sql, Object... params) throws SQLException {
        return update(conn,false,sql,params);
    }

    //没有传递Connection的情况，靠DataSource来获取链接，
    //采取的方式是自己执行完update之后自己关闭
    public int update(String sql, Object params) throws SQLException {
        Connection con=this.prepareConnection();
        return this.update(con,true,sql,new Object[]{params});
    }

    public int update(String sql, Object... parmas) throws SQLException {
        Connection con=this.prepareConnection();
        return this.update(con,true,sql,parmas);
    }

    private int update(Connection con, boolean closeConn, String sql, Object... params) throws SQLException {
        //为了健壮代码(robust)，先进行一系列的检查，守护代码，卫士代码
        if (con == null) {
            throw new SQLException("Null connection");
        }
        if (sql == null) {
            if (closeConn) {
                close(con);
            }
            throw new SQLException("Null SQL statement");
        }
        PreparedStatement stmt=null;
        int rows=0;

        try {
            stmt=this.preparedStatement(con,sql);
            this.fillStatement(stmt,params);
            rows=stmt.executeUpdate();
        } catch (SQLException throwables) {
            this.rethrow(throwables,sql,params);
        }finally {
            close(stmt);
            if (closeConn) {
                close(con);
            }
        }
        return rows;
    }

    public <T> T query(String sql, MiniResultHandler<T> rsh, Object... parmas) throws SQLException {
        Connection con=this.prepareConnection();
        return this.<T>query(con,true,sql,rsh,parmas);
    }

    public <T> T query(String sql,MiniResultHandler<T> rsh) throws SQLException {
        Connection con=this.prepareConnection();
        return this.<T>query(con,true,sql,rsh,(Object[])null );
    }

    public <T> T query(Connection con,boolean closeConn,String sql,MiniResultHandler<T> rsh,Object...params) throws SQLException {
        if (con == null) {
            throw new SQLException("Null connection");
        }
        if (sql == null) {
            if (closeConn) {
                close(con);
            }
            throw new SQLException("Null SQL statement");
        }
        if (rsh == null) {
            if (closeConn) {
                close(con);
            }
            throw new SQLException("Null ResultSetHandler");
        }
        PreparedStatement stmt=null;
        ResultSet rs=null;
        T result=null;
        //获取数据库连接和SQL语句
        try {
            stmt=this.preparedStatement(con,sql);
            //将参数填充
            this.fillStatement(stmt,params);
            //结果集执行查询操作
            rs=stmt.executeQuery();
            result= rsh.handle(rs);
        } catch (SQLException throwables) {
            this.rethrow(throwables,sql,params);
        }finally {
            try {
                close(rs);
            } finally {
                close(stmt);
                if (closeConn) {
                    close(con);
                }
            }
        }
        return result;
    }

    public <T> T insert(String sql,MiniResultHandler<T> rsh) throws SQLException {
        return insert(this.prepareConnection(),true,sql,rsh,(Object[])null );
    }

    public <T> T insert(String sql,MiniResultHandler<T>rsh,Object...params) throws SQLException {
        return insert(this.prepareConnection(),true,sql,rsh,params);
    }

    public <T> T insert(Connection con,String sql,MiniResultHandler<T> rsh) throws SQLException {
        return insert(con,false,sql,rsh,(Object[])null );
    }

    public <T> T insert(Connection con,String sql,MiniResultHandler<T>rsh,Object...params) throws SQLException {
        return insert(con,false,sql,rsh,params);
    }

    private  <T> T insert(Connection con,boolean closeCon,String sql,MiniResultHandler<T> rsh,Object...params) throws SQLException {
        if (con == null) {
            con.close();
        }
        if (sql == null) {
            if (closeCon) {
                close(con);
            }
            throw new SQLException("SQL Statement Null");
        }
        if (rsh == null) {
            if (closeCon) {
                close(con);
            }
            throw new SQLException("Null ResultSetHandler");
        }

        PreparedStatement ps=null;
        T generateKeys=null;
        try {
            ps=con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            this.fillStatement(ps,params);
            ps.executeUpdate();
            ResultSet rs = ps.getGeneratedKeys();
            generateKeys=rsh.handle(rs);
        } catch (SQLException throwables) {
            this.rethrow(throwables,sql,params);
        }finally {
            close(ps);
            if (closeCon) {
                close(con);
            }
        }
        return generateKeys;
    }
}
